#include "OoO/backend.h"
namespace config
{
    const Value<uint64_t> ideal_backend_width = {"ideal_backend_width", 2, "ideal backend width"};
} // namespace config

IdealBackend::IdealBackend() : Backend()
{
    reset();
}
IdealBackend::~IdealBackend()
{
}
void IdealBackend::run()
{
    for (uint64_t i = 0; i < config::ideal_backend_width; i++)
    {
        auto fop = frontend->fetch_op();
        if (fop.op == NULL)
        {
            break;
        }
        if (fop.op->valid == false)
        {
            sim_finish = true;
            break;
        }

        bool taken = fop.op->trace.branch_taken;
        word_t addr = fop.op->trace.pc;
        word_t target = fop.op->trace.branch_pc;
        BranchType type = BranchType::NONE;
        auto trace = fop.op->trace;
        auto opcode = fop.op->trace.opcode;
        if (opcode == OP_CBR)
        {
            type = BranchType::Branch;
        }
        else if (opcode == OP_JMP)
        {
            int8_t rs1 = trace.src[0];
            int8_t rd = trace.dst;
            bool rs1_link = rs1 == 1 || rs1 == 5;
            bool rd_link = rd == 1 || rd == 5;
            bool jal = rs1 == -1;
            type = BranchType::Jump;
            if (jal && rd_link)
            {
                type = BranchType::Call;
            }
            else
            {
                if (!rd_link && rs1_link)
                {
                    type = BranchType::Ret;
                }
                else if (rd_link && !rs1_link)
                {
                    type = BranchType::Call;
                }
                else if (rd_link && rs1_link && rd != rs1)
                {
                    type = BranchType::CallRet;
                }
                else if (rd_link && rs1_link && rd == rs1)
                {
                    type = BranchType::Call;
                }
            }
        }

        BPU_Res correct_res = {0};
        if (fop.bpred_miss || type != BranchType::NONE)
        {
            if (fop.bpu_res.valid)
            {
                correct_res = fop.bpu_res;
            }
            else
            {
                switch (type)
                {
                case BranchType::Jump:
                    break;
                case BranchType::Branch:
                    correct_res.btb_sc = taken ? 2 : 1;
                    break;
                case BranchType::Call:
                case BranchType::Ret:
                case BranchType::CallRet:

                    break;
                default:
                    SPDLOG_ERROR("[Backend] should not update NONE branch instruction to frontend");
                    break;
                }
            }
            correct_res.taken = taken;
            correct_res.target = target;
            correct_res.valid = true;
            correct_res.type = type;
        }
        if (fop.bpred_miss)
        {
            frontend->redirect(addr, correct_res);
        }
        else if (type != BranchType::NONE)
        {
            correct_res.ras_valid = false;
            frontend->update(addr, correct_res);
        }

        free_op(fop.op);
        inst_cnt++;
    }
    cycle++;
}
void IdealBackend::reset()
{
    sim_finish = false;
    inst_cnt = 0;
    cycle = 0;
}

#define ARCH_REG_NUM 32

namespace config
{
    const Value<uint64_t> OoO_backend_prf_num = {"OoO_backend_prf_num", 40, "OoO backend physical regfile num"};
    const Value<uint64_t> OoO_backend_rename_width = {"OoO_backend_rename_width", 2, "OoO backend rename width"};
    const Value<uint64_t> OoO_backend_commit_width = {"OoO_backend_commit_width", 2, "OoO backend commit width"};
    const Value<uint64_t> OoO_backend_rob_size = {"OoO_backend_rob_size", 20, "OoO backend rob size"};
    const Value<uint64_t> OoO_backend_alu_num = {"OoO_backend_alu_num", 2, "OoO backend alu num"};
    const Value<uint64_t> OoO_backend_mdu_num = {"OoO_backend_mdu_num", 1, "OoO backend mdu num"};
    const Value<uint64_t> OoO_backend_bru_num = {"OoO_backend_bru_num", 1, "OoO backend bru num"};
    const Value<uint64_t> OoO_backend_lsu_num = {"OoO_backend_lsu_num", 1, "OoO backend lsu num"};
    const Value<uint64_t> OoO_backend_alu_issueq_size = {"OoO_backend_alu_issueq_size", 8, "OoO backend alu issue queue size"};
    const Value<uint64_t> OoO_backend_mdu_issueq_size = {"OoO_backend_mdu_issueq_size", 2, "OoO backend mdu issue queue size"};
    const Value<uint64_t> OoO_backend_bru_issueq_size = {"OoO_backend_bru_issueq_size", 1, "OoO backend bru issue queue size"};
    const Value<uint64_t> OoO_backend_lsu_issueq_size = {"OoO_backend_lsu_issueq_size", 4, "OoO backend lsu issue queue size"};
    const Value<uint64_t> OoO_backend_ldq_size = {"OoO_backend_ldq_size", 8, "OoO backend laod queue size"};
    const Value<uint64_t> OoO_backend_stq_size = {"OoO_backend_stq_size", 8, "OoO backend store queue size"};
    const Switch OoO_backend_enable_OoO_load = {"OoO_backend_enable_OoO_load", "OoO backend enable OoO load"};
    const Switch OoO_backend_enable_ST_to_LD_forward = {"OoO_backend_enable_ST_to_LD_forward", "OoO backend store to load forward"};
    const Switch OoO_backend_redirect_branch_in_commit = {"OoO_backend_redirect_branch_in_commit", "OoO backend redirect miss predicted branch in commit stage"};
    const Switch OoO_backend_conflict_check_byte = {"OoO_backend_conflict_check_byte", "OoO backend load store check per byte"};
    ;
} // namespace config

OoOBackend::OoOBackend() : Backend(), prf(config::OoO_backend_prf_num), rat(ARCH_REG_NUM),
                           frontend_stall("Frontend-stall"), rob_full("Rob-full"), prf_full("Prf-full"), issue_stall("Issue-stall"),
                           iq_alu_full("IQ-full-alu"), iq_mdu_full("IQ-full-mdu"), iq_bru_full("IQ-full-bru"), iq_lsu_full("IQ-full-lsu"),
                           hazard_alu("RAW-stall-alu"), hazard_mdu("RAW-stall-mdu"), hazard_bru("RAW-stall-bru"), hazard_lsu("RAW-stall-lsu"),
                           commit_bound("Commit-bound"), ldq_full("LDQ-full"), stq_full("STQ-full"),
                           mem_hazard("Memory-hazard"), ld2ld_hazard("LD-to-LD-Hazard"), ld2st_hazard("LD-to-ST-Hazard"), st2ld_hazard("ST-to-LD-Hazard"), st2st_hazard("ST-to-ST-Hazard"),
                           mem_byte_hazard("Memory-Byte-hazard"), ld2ld_byte_hazard("LD-to-LD-Byte-Hazard"), ld2st_byte_hazard("LD-to-ST-Byte-Hazard"), st2ld_byte_hazard("ST-to-LD-Byte-Hazard"), st2st_byte_hazard("ST-to-ST-Byte-Hazard"),
                           st2ld_forward("ST-to-LD-forward"), total_mem_hazard("Total-mem-hazard"), rob_commit_stall_by_store("rob_commit_stall_by_store")
{
    reset();
}
OoOBackend::~OoOBackend()
{
}
void OoOBackend::run()
{
    SPDLOG_TRACE("[Commit] Cycle {}", cycle);
    Commit();
    SPDLOG_TRACE("[Execute] Cycle {}", cycle);
    Execute();
    SPDLOG_TRACE("[Issue] Cycle {}", cycle);
    Issue();
    SPDLOG_TRACE("[Dispatch] Cycle {}", cycle);
    Dispatch();
    SPDLOG_TRACE("[Rename] Cycle {}", cycle);
    Rename();
    cycle++;
}
void OoOBackend::reset()
{
    sim_finish = false;

    for (size_t i = 0; i < ARCH_REG_NUM; i++)
    {
        rat[i].preg_idx = i;
    }
    freelist.resize(config::OoO_backend_prf_num);
    for (size_t i = ARCH_REG_NUM; i < config::OoO_backend_prf_num; i++)
    {
        FreeListEntry entry = {.preg_idx = (uint32_t)i};
        freelist.push_back(entry);
    }

    for (auto &prf_entry : prf)
    {
        prf_entry.valid = true;
    }

    rops.resize(config::OoO_backend_rename_width);

    robs.resize(config::OoO_backend_rob_size);

    iq_alu.resize(config::OoO_backend_alu_issueq_size);
    iq_mdu.resize(config::OoO_backend_mdu_issueq_size);
    iq_bru.resize(config::OoO_backend_bru_issueq_size);
    iq_lsu.resize(config::OoO_backend_lsu_issueq_size);
    ex_alu.resize(config::OoO_backend_alu_num);
    ex_mdu.resize(config::OoO_backend_mdu_num);
    ex_bru.resize(config::OoO_backend_bru_num);
    ex_lsu.resize(config::OoO_backend_lsu_num);

    ldq.resize(config::OoO_backend_ldq_size);
    stq.resize(config::OoO_backend_stq_size);
    cop_allocator.resize(256);

    fu_table[OP_NOP] = {FU_Type::ALU, 1, "ALU"};
    fu_table[OP_ADD] = {FU_Type::ALU, 1, "ALU"};
    fu_table[OP_CBR] = {FU_Type::BRU, 1, "BRU"};
    fu_table[OP_JMP] = {FU_Type::BRU, 1, "BRU"};
    fu_table[OP_MUL] = {FU_Type::MDU, 3, "MDU"};
    fu_table[OP_DIV] = {FU_Type::MDU, 17, "MDU"};
    fu_table[OP_LD] = {FU_Type::LSU, 1, "LSU"};
    fu_table[OP_ST] = {FU_Type::LSU, 1, "LSU"};

    bop_allocator.resize(1024);
}
void OoOBackend::Rename()
{
    auto freelist_size = freelist.size();
    auto rop_size = rops.size();
    auto rob_size = robs.size();
    size_t rename_size = config::OoO_backend_rename_width;

    if (rename_size > freelist_size)
    {
        rename_size = freelist_size;
        prf_full++;
    }
    if (rename_size + rop_size > config::OoO_backend_rename_width)
    {
        rename_size = config::OoO_backend_rename_width - rop_size;
        issue_stall++;
    }
    if (rename_size + rob_size > config::OoO_backend_rob_size)
    {
        rename_size = config::OoO_backend_rob_size - rob_size;
        rob_full++;
    }

    for (size_t i = 0; i < rename_size; i++)
    {
        RenamedOP rop;
        rop.fop = frontend->fetch_op();
        if (rop.fop.op == NULL)
        {
            frontend_stall++;
            break;
        }
        auto op = rop.fop.op;
        auto rs1 = op->trace.src[0];
        auto rs2 = op->trace.src[1];
        auto dst = op->trace.dst;
        if (rs1 > 0)
        {
            rop.prs[0] = rat[rs1].preg_idx;
        }
        else
        {
            rop.prs[0] = -1;
        }

        if (rs2 > 0)
        {
            rop.prs[1] = rat[rs2].preg_idx;
        }
        else
        {
            rop.prs[1] = -1;
        }

        if (dst > 0)
        {
            rop.free_pdst = rat[dst].preg_idx;
            auto freelist_entry = freelist.front();
            rop.pdst = freelist_entry.preg_idx;
            freelist.pop_front();
            rat[dst].preg_idx = rop.pdst;
            prf[rop.pdst].valid = false;
            SPDLOG_TRACE("[Rename] Sleep pdst {} free_pdst {} by op {}", rop.pdst, rop.free_pdst, rop.fop.op->id);
        }
        else
        {
            rop.pdst = -1;
            rop.free_pdst = -1;
        }

        rop.renamed = true;

        auto bop = bop_allocator.malloc();
        // auto bop = new BackendOP;
        bop->rop = rop;
        bop->cop = NULL;
        rops.push_back(bop);
        ROB_Entry rob = {.bop = bop, .finish = false, .need_store = false};
        SPDLOG_TRACE("[Rename] Rob push");
        bop->rob_index = robs.push(rob);
        SPDLOG_TRACE("[Rename] Rename op {}", rop.fop.op->id);
    }
}
void OoOBackend::Dispatch()
{
    auto it = rops.begin();
    while (it != rops.end())
    {
        auto bop = it->data;
        auto rop = bop->rop;
        if (!rop.renamed)
            break;
        auto fu = fu_table[rop.fop.op->trace.opcode];
        bool rop_dispatched = false;

        switch (fu.type)
        {
        case FU_Type::ALU:
            if (iq_alu.size() < config::OoO_backend_alu_issueq_size)
            {
                rop_dispatched = true;
                iq_alu.push_back(bop);
            }
            else
            {
                iq_alu_full++;
            }
            break;
        case FU_Type::BRU:
            if (iq_bru.size() < config::OoO_backend_bru_issueq_size)
            {
                rop_dispatched = true;
                iq_bru.push_back(bop);
            }
            else
            {
                iq_bru_full++;
            }
            break;
        case FU_Type::MDU:
            if (iq_mdu.size() < config::OoO_backend_mdu_issueq_size)
            {
                rop_dispatched = true;
                iq_mdu.push_back(bop);
            }
            else
            {
                iq_mdu_full++;
            }
            break;
        case FU_Type::LSU:
            if (iq_lsu.size() < config::OoO_backend_lsu_issueq_size)
            {
                rop_dispatched = true;
                iq_lsu.push_back(bop);
            }
            else
            {
                iq_lsu_full++;
            }
            break;
        default:
            break;
        }
        if (rop_dispatched)
        {
            SPDLOG_TRACE("[Dispatch] op {} dispatched to {}", rop.fop.op->id, fu.name);
            it = rops.erase(it);
        }
        else
        {
            SPDLOG_TRACE("[Dispatch] op {} can not be dispatched to {}", rop.fop.op->id, fu.name);
            it = it->next;
        }
    }
}
void OoOBackend::Issue()
{
    /* ALU */
    SPDLOG_TRACE("[Issue] ALU");
    for (auto it = iq_alu.begin(); it != iq_alu.end();)
    {
        auto op_in_alu = ex_alu.size();
        auto bop = it->data;
        auto rop = bop->rop;
        if (op_in_alu < config::OoO_backend_alu_num)
        {
            if (rop_ready(rop))
            {
                SPDLOG_TRACE("[Issue] issue ALU op {}", rop.fop.op->id);
                ex_alu.push_back({bop, cycle});
                it = iq_alu.erase(it);
            }
            else
            {
                // it++;
                it = it->next;
            }
        }
        else
        {
            break;
        }
    }
    /* BRU */
    SPDLOG_TRACE("[Issue] BRU");
    for (auto it = iq_bru.begin(); it != iq_bru.end();)
    {
        auto op_in_bru = ex_bru.size();
        auto bop = it->data;
        auto rop = bop->rop;
        if (op_in_bru < config::OoO_backend_bru_num)
        {
            if (rop_ready(rop))
            {
                SPDLOG_TRACE("[Issue] issue BRU op {}", rop.fop.op->id);
                ex_bru.push_back({bop, cycle});
                it = iq_bru.erase(it);
            }
            else
            {
                it = it->next;
                /* Force BRU inorder when redirect in execute stage. */
                if (!config::OoO_backend_redirect_branch_in_commit)
                {
                    break;
                }
            }
        }
        else
        {
            break;
        }
    }
    /* MDU */
    SPDLOG_TRACE("[Issue] MDU");
    for (auto it = iq_mdu.begin(); it != iq_mdu.end();)
    {
        auto op_in_mdu = ex_mdu.size();
        auto bop = it->data;
        auto rop = bop->rop;
        if (op_in_mdu < config::OoO_backend_mdu_num)
        {
            if (rop_ready(rop))
            {
                SPDLOG_TRACE("[Issue] issue MDU op {}", rop.fop.op->id);
                ex_mdu.push_back({bop, cycle});
                it = iq_mdu.erase(it);
            }
            else
            {
                it = it->next;
            }
        }
        else
        {
            break;
        }
    }
    /* LSU */
    SPDLOG_TRACE("[Issue] LSU");
    for (auto it = iq_lsu.begin(); it != iq_lsu.end();)
    {
        auto op_in_lsu = ex_lsu.size();
        auto bop = it->data;
        auto rop = bop->rop;
        if (op_in_lsu < config::OoO_backend_lsu_num)
        {
            if (rop_ready(rop))
            {
                SPDLOG_TRACE("[Issue] issue LSU op {}", rop.fop.op->id);
                auto cop = cop_allocator.malloc();
                bop2cop(cop, bop);
                bop->cop = cop;
                ex_lsu.push_back({bop, cycle});
                it = iq_lsu.erase(it);
            }
            else
            {
                SPDLOG_TRACE("[Issue] can not issue LSU op {}", rop.fop.op->id);
                /* Force LSU inorder. */
                break;
            }
        }
        else
        {
            break;
        }
    }

    /* Functional unit will not operate due to RAW hazard */
    if (iq_alu.size() && ex_alu.size() < config::OoO_backend_alu_num)
    {
        hazard_alu++;
    }
    if (iq_mdu.size() && ex_mdu.size() < config::OoO_backend_mdu_num)
    {
        hazard_mdu++;
    }
    if (iq_bru.size() && ex_bru.size() < config::OoO_backend_bru_num)
    {
        hazard_bru++;
    }
    if (iq_lsu.size() && ex_lsu.size() < config::OoO_backend_lsu_num)
    {
        hazard_lsu++;
    }
}
void OoOBackend::Execute()
{
    /* ALU */
    for (auto it = ex_alu.begin(); it != ex_alu.end();)
    {
        auto [bop, rop_cycle] = it->data;
        auto rop = bop->rop;
        auto fu = fu_table[rop.fop.op->trace.opcode];
        if (rop_cycle + fu.latnecy <= cycle)
        {
            robs[bop->rob_index].finish = true;
            SPDLOG_TRACE("[Execute] Execute op {} in {}", rop.fop.op->id, fu.name);
            wakeup(rop);
            it = ex_alu.erase(it);
        }
        else
        {
            // it++;
            it = it->next;
        }
    }

    /* BRU */
    for (auto it = ex_bru.begin(); it != ex_bru.end();)
    {
        auto [bop, rop_cycle] = it->data;
        auto rop = bop->rop;
        auto fu = fu_table[rop.fop.op->trace.opcode];
        if (rop_cycle + fu.latnecy <= cycle)
        {
            if (!config::OoO_backend_redirect_branch_in_commit)
                execute_bru_op(rop);
            robs[bop->rob_index].finish = true;
            SPDLOG_TRACE("[Execute] Execute op {} in {}", rop.fop.op->id, fu.name);
            wakeup(rop);
            it = ex_bru.erase(it);
        }
        else
        {
            // it++;
            it = it->next;
        }
    }

    /* MDU */
    for (auto it = ex_mdu.begin(); it != ex_mdu.end();)
    {
        auto [bop, rop_cycle] = it->data;
        auto rop = bop->rop;
        auto fu = fu_table[rop.fop.op->trace.opcode];
        if (rop_cycle + fu.latnecy <= cycle)
        {
            robs[bop->rob_index].finish = true;
            SPDLOG_TRACE("[Execute] Execute op {} in {}", rop.fop.op->id, fu.name);
            wakeup(rop);
            it = ex_mdu.erase(it);
        }
        else
        {
            // it++;
            it = it->next;
        }
    }

    bool pending_load = false;
    /* LSU */
    for (auto it = ex_lsu.begin(); it != ex_lsu.end();)
    {
        auto [bop, rop_cycle] = it->data;
        auto rop = bop->rop;
        auto fu = fu_table[rop.fop.op->trace.opcode];
        if (rop_cycle + fu.latnecy <= cycle)
        {
            /* Memory Hazard Check */
            bool conflict = mem_hazard_check(bop->cop);
            SPDLOG_TRACE("[Execute] LSU Memory Hazard Check conflict {}", conflict);
            if (!conflict)
            {
                SPDLOG_DEBUG("[Execute] LDQ {} STQ {}", ldq.size(), stq.size());
                if (bop->cop->cmd == CacheReqCmd::LOAD)
                {
                    if (!ldq.full())
                    {
                        if (cache->send(bop->cop))
                        {
                            bop->cop->id = (uintptr_t)ldq.push_back(bop);
                            SPDLOG_DEBUG("[Execute] Execute load op {} in {}", rop.fop.op->id, fu.name);
                            it = ex_lsu.erase(it);
                            continue;
                        }
                    }
                    else
                    {
                        ldq_full++;
                    }
                }
                else if (bop->cop->cmd == CacheReqCmd::STORE && !pending_load)
                {
                    if (!stq.full())
                    {
                        stq.push(bop);
                        robs[bop->rob_index].finish = true;
                        robs[bop->rob_index].need_store = true;
                        robs[bop->rob_index].wait_store = false;
                        SPDLOG_DEBUG("[Execute] Execute store op {} in {}", rop.fop.op->id, fu.name);
                        it = ex_lsu.erase(it);
                        continue;
                    }
                    else
                    {
                        stq_full++;
                    }
                }
            }
            else
            {
                total_mem_hazard++;
            }

            if (bop->cop->cmd == CacheReqCmd::LOAD)
            {
                pending_load = true;
                if (!config::OoO_backend_enable_OoO_load)
                {
                    break;
                }
            }
            else if (bop->cop->cmd == CacheReqCmd::STORE)
            {
                // Force Store Inorder
                break;
            }
        }
        it = it->next;
    }
}
void OoOBackend::Commit()
{
    size_t commit_num = 0;
    while (commit_num < config::OoO_backend_commit_width && robs.size() > 0)
    {
        auto &rob = robs.front();

        if (rob.finish)
        {
            if (rob.need_store)
            {
                assert(stq.size() > 0);
                assert(stq.front()->rob_index == rob.bop->rob_index);
                if (!cache->send(rob.bop->cop))
                {
                    break;
                }
                else
                {
                    rob.need_store = false;
                    rob.wait_store = true;
                    stq.pop();
                    break;
                }
            }
            if (rob.wait_store)
            {
                break;
            }
            if (rob.bop->cop)
            {
                cop_allocator.free(rob.bop->cop);
            }
            auto rop = rob.bop->rop;
            SPDLOG_TRACE("[Commit] Commit op {} in cycle {}", rop.fop.op->id, cycle);
            if (rop.fop.op->valid == false)
            {
                sim_finish = true;
            }
            if (rop.free_pdst < config::OoO_backend_prf_num)
            {
                freelist.push_back({rop.free_pdst});
                SPDLOG_TRACE("[Commit] Push pdst {} to freelist by op {}", rop.free_pdst, rop.fop.op->id);
            }

            if (config::OoO_backend_redirect_branch_in_commit)
                execute_bru_op(rop);
            free_op(rop.fop.op);
            // delete rob.bop;
            bop_allocator.free(rob.bop);
            SPDLOG_TRACE("[Commit] Rob pop_front");
            robs.pop();
            inst_cnt++;
            commit_num++;
        }
        else
        {
            if (rob.need_store)
                rob_commit_stall_by_store++;
            SPDLOG_TRACE("[Commit] op {} waitting finish to commit", rob.bop->rop.fop.op->id);
            break;
        }
    }
    if (commit_num == config::OoO_backend_commit_width && robs.size() > 0 && robs.front().finish)
    {
        commit_bound++;
    }
}
bool OoOBackend::rop_ready(RenamedOP &rop)
{
    bool ready = true;
    auto prs1 = rop.prs[0];
    auto prs2 = rop.prs[1];
    if (prs1 < config::OoO_backend_prf_num)
    {
        ready &= prf[prs1].valid;
    }
    if (prs2 < config::OoO_backend_prf_num)
    {
        ready &= prf[prs2].valid;
    }
    return ready;
}

void OoOBackend::wakeup(RenamedOP &rop)
{
    if (rop.pdst < config::OoO_backend_prf_num)
    {
        prf[rop.pdst].valid = true;
        SPDLOG_TRACE("[Wakeup] wakeup pdst {} by op {}", rop.pdst, rop.fop.op->id);
    }
}
void OoOBackend::bop2cop(CacheOP *cop, BackendOP *bop)
{
    auto op = bop->rop.fop.op;
    cop->addr = op->trace.mem_access_addr;
    cop->size = op->trace.mem_access_size;
    cop->id = bop->rob_index;
    // if (op->trace.mmio_access)
    // {
    //     cop->cmd = CacheReqCmd::MMIO;
    // }
    // else
    {
        if (op->trace.opcode == OP_LD)
        {
            cop->cmd = CacheReqCmd::LOAD;
        }
        else if (op->trace.opcode == OP_ST)
        {
            cop->cmd = CacheReqCmd::STORE;
        }
        else
        {
            SPDLOG_ERROR("[Backend] should not prase a non memory op {} to cache op", op->id);
            cop->cmd = CacheReqCmd::NOP;
        }
    }
}
bool OoOBackend::mem_hazard_check(CacheOP *cop)
{
    assert(cop);
    const word_t mask = ~((1ull << sizeof(word_t)) - 1ull);
    auto target_addr = cop->addr;
    auto target_size = cop->size;
    auto it = ldq.begin();
    bool conflict = false;
    bool byte_conflict = false;
    if (cop->cmd == CacheReqCmd::STORE)
        return false;
    if (!config::OoO_backend_enable_OoO_load)
    {
        while (it != ldq.end())
        {
            auto addr = it->data->cop->addr;
            auto size = it->data->cop->size;
            if ((addr & mask) == (target_addr & mask))
            {
                conflict = true;
                if (addr_hazard_check(target_addr, target_size, addr, size))
                {
                    byte_conflict = true;
                    if (cop->cmd == CacheReqCmd::LOAD)
                    {
                        ld2ld_byte_hazard++;
                    }
                    else
                    {
                        ld2st_byte_hazard++;
                    }
                }
                if (cop->cmd == CacheReqCmd::LOAD)
                {
                    ld2ld_hazard++;
                }
                else
                {
                    ld2st_hazard++;
                }
            }
            it = it->next;
        }
    }

    auto index = (size_t)stq.begin();
    for (size_t i = 0; i < stq.size(); i++)
    {
        auto addr = stq[index]->cop->addr;
        auto size = stq[index]->cop->size;
        if ((addr & mask) == (target_addr & mask))
        {
            conflict = true;
            byte_conflict = true;
            if (store_load_forward(cop))
            {
                conflict = false;
                byte_conflict = false;
                st2ld_forward++;
            }
            if (addr_hazard_check(target_addr, target_size, addr, size))
            {
                if (cop->cmd == CacheReqCmd::LOAD)
                {
                    st2ld_byte_hazard++;
                }
                else
                {
                    st2st_byte_hazard++;
                }
            }
            if (cop->cmd == CacheReqCmd::LOAD)
            {
                st2ld_hazard++;
            }
            else
            {
                st2st_hazard++;
            }
        }
        index = (index + 1) % stq.max_size();
    }
    if (conflict)
    {
        mem_hazard++;
    }
    if (byte_conflict)
    {
        mem_byte_hazard++;
    }
    if (config::OoO_backend_conflict_check_byte)
    {
        return byte_conflict;
    }
    else
    {
        return conflict;
    }
}

bool OoOBackend::store_load_forward(CacheOP *cop)
{
    assert(cop);
    if (!config::OoO_backend_enable_ST_to_LD_forward)
        return false;
    if (cop->cmd != CacheReqCmd::LOAD)
        return false;
    const word_t mask = ~((1ull << sizeof(word_t)) - 1ull);
    auto index = (size_t)stq.end();
    word_t target_addr = cop->addr;
    word_t target_size = cop->size;

    for (size_t i = 0; i < stq.size(); i++)
    {
        if (index == 0)
            index = stq.max_size() - 1;
        else
            index--;
        word_t addr = stq[index]->cop->addr;
        word_t size = stq[index]->cop->size;
        if ((target_addr & mask) != (addr & mask))
            continue;
        word_t mask1 = ((1 << target_size) - 1) << (target_addr % 8);
        word_t mask2 = ((1 << size) - 1) << (addr % 8);
        if ((mask1 | mask2) == mask2)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    return false;
}
void OoOBackend::execute_bru_op(RenamedOP &rop)
{
    auto fop = rop.fop;
    bool taken = fop.op->trace.branch_taken;
    word_t addr = fop.op->trace.pc;
    word_t target = fop.op->trace.branch_pc;
    BranchType type = BranchType::NONE;
    auto trace = fop.op->trace;
    auto opcode = fop.op->trace.opcode;
    if (opcode == OP_CBR)
    {
        type = BranchType::Branch;
    }
    else if (opcode == OP_JMP)
    {
        int8_t rs1 = trace.src[0];
        int8_t rd = trace.dst;
        bool rs1_link = rs1 == 1 || rs1 == 5;
        bool rd_link = rd == 1 || rd == 5;
        bool jal = rs1 == -1;
        type = BranchType::Jump;
        if (jal && rd_link)
        {
            type = BranchType::Call;
        }
        else
        {
            if (!rd_link && rs1_link)
            {
                type = BranchType::Ret;
            }
            else if (rd_link && !rs1_link)
            {
                type = BranchType::Call;
            }
            else if (rd_link && rs1_link && rd != rs1)
            {
                type = BranchType::CallRet;
            }
            else if (rd_link && rs1_link && rd == rs1)
            {
                type = BranchType::Call;
            }
        }
    }

    BPU_Res correct_res = {0};
    if (fop.bpred_miss || type != BranchType::NONE)
    {
        if (fop.bpu_res.valid)
        {
            correct_res = fop.bpu_res;
        }
        else
        {
            switch (type)
            {
            case BranchType::Jump:
                break;
            case BranchType::Branch:
                correct_res.btb_sc = taken ? 2 : 1;
                break;
            case BranchType::Call:
            case BranchType::Ret:
            case BranchType::CallRet:

                break;
            default:
                SPDLOG_ERROR("[Backend] should not update NONE branch instruction to frontend");
                break;
            }
        }
        correct_res.taken = taken;
        correct_res.target = target;
        correct_res.valid = true;
        correct_res.type = type;
    }
    if (fop.bpred_miss)
    {
        frontend->redirect(addr, correct_res);
    }
    else if (type != BranchType::NONE)
    {
        correct_res.ras_valid = false;
        frontend->update(addr, correct_res);
    }
}

bool OoOBackend::recv(CacheOP *cop)
{
    if (cop->cmd == CacheReqCmd::LOAD)
    {
        auto it = ((ConstList<BackendOP *>::Node *)(cop->id));
        auto bop = it->data;
        robs[bop->rob_index].finish = true;
        wakeup(bop->rop);
        ldq.erase(it);
        SPDLOG_INFO("[Backend] Recv load cache op id {} cmd {}", bop->rop.fop.op->id, cop->cmd);
    }
    else if (cop->cmd == CacheReqCmd::STORE)
    {
        robs[cop->id].wait_store = false;
        SPDLOG_DEBUG("[Backend] Recv store cache op id {} cmd {}", bop->rop.fop.op->id, cop->cmd);
    }
    else
    {
        SPDLOG_DEBUG("[Backend] Recv other cache op pointer {} cmd {}", cop->id, cop->cmd);
    }
    return true;
}